package org.fjsei.yewu.resolver.sei.inspect;

import md.cm.flow.ApprovalStmRepository;
import md.cm.unit.Units;
import md.specialEqp.Eqp;
import md.specialEqp.Equipments;
import md.specialEqp.Report;
import md.specialEqp.ReportRepository;
import md.specialEqp.inspect.*;
import md.specialEqp.type.Pipeline;
import md.system.AuthorityRepository;
import md.system.User;
import md.system.UserRepository;
import org.fjsei.yewu.exception.CommonGraphQLException;
import org.fjsei.yewu.input.DetailInput;
import org.fjsei.yewu.input.FeeItemInput;
import org.fjsei.yewu.payload.TaskComResp;
import org.fjsei.yewu.resolver.Comngrql;
import org.fjsei.yewu.security.JwtUserDetailsService;
import org.fjsei.yewu.util.Tool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

import static md.specialEqp.BusinessCat_Enum.TEST;


//实际相当于REST接口的API: controller 目录的功能层次含义;
//这个类名字整个软件之内范围 不能重复 简名字！ baseMutation 是组件注入的名字。
//Mutation/Query本组内函数名字不能同名， 底层上做了HashMap找接口函数。
//GraphQL有非常重要的一个特点：强类型,自带graphQL基本类型标量Int, Float, String, Boolean和ID。　https://segmentfault.com/a/1190000011263214
//乐观锁确保任何更新或删除不会被无意地覆盖或丢失。悲观锁会在数据库级别上锁实体会引起DB级死锁。 https://blog.csdn.net/neweastsun/article/details/82318734

/**
 *接口服务层次; 类似于 class XxServiceImpl implements XxService{ }
 */
@Controller
public class IspMgrMutation extends Comngrql {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    //@jakarta.annotation.Resource    @Qualifier("Xxx")
    //private OrderRepository orderRepository;  ？  可以注入的@Resource；
    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;
    @Autowired
    private Equipments eQPRepository;
    @Autowired
    private IspRepository iSPRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private TaskRepository taskRepository;
    @Autowired
    private ReportRepository reportRepository;
    @Autowired
    private Units units;
    @Autowired
    private AuthorityRepository authorityRepository;
    @Autowired private ApprovalStmRepository approvalStmRepository;
    @Autowired private DetailRepository detailRepository;

    @PersistenceContext(unitName = "entityManagerFactorySei")
    private EntityManager emSei;                //EntityManager相当于hibernate.Session：

//    @Autowired
//    private final JwtTokenUtil jwtTokenUtil=new JwtTokenUtil();


    @Transactional
    public Isp newISP(UUID devId) {
        if(!emSei.isJoinedToTransaction())      emSei.joinTransaction();
        Isp isp = new Isp();
        Eqp eQP = eQPRepository.findById(devId).orElse(null);
        Assert.isTrue(eQP != null,"未找到eQP:"+eQP);
        isp.setDev(eQP);
        iSPRepository.save(isp);
        return isp;
    }
    @Transactional
    public Isp buildISP(UUID devId, UUID taskId, String username) {
//        if(!emSei.isJoinedToTransaction())      emSei.joinTransaction();
//        User user = userRepository.findByUsername(username);
//        if(user == null)     throw new CommonGraphQLException("没有该账户"+username, (long)0);
//        Task task = taskRepository.findById(taskId).orElse(null);
//        if(task == null)     throw new CommonGraphQLException("没有该任务单", taskId.toString());
//        Eqp eQP = eQPRepository.findById(devId).orElse(null);
//        if(eQP == null)     throw new CommonGraphQLException("没有该设备", devId.toString());
//        Isp isp = new Isp();
//        isp.setDev(eQP);
//        Assert.isTrue(false,"todo:不兼容");
//        //todo:isp.setTask(task);
//        Set<User> ispMen= new HashSet<User>();
//        //  ispMens.stream().forEach(item ->
//        ispMen.add(user);
//        isp.setIspMen(ispMen);
//        //没有底下1行，在缓存时间之内，若以EQP起头查询关联将无法查到新isp，刷新URL也看不见新ISP，但getISPofDevTask查就可见。后端侧cache时间到了就都行了。
//
//        //todo:task.getIsps().add(isp);
//        //因为前端getDeviceSelf接口关联查询后读取路径是EQP.task.isps;而不是读EQP.isps字段，所以下面1行可以不搞；和接口使用者读的字段关联路径需求有直接关系。
//        //eQP.getIsps().add(isp);
//        iSPRepository.save(isp);
        return null;//isp;
    }

    @Transactional
    public Isp setISPispMen(UUID id, List<UUID> ispMens) {
//        if(!emSei.isJoinedToTransaction())      emSei.joinTransaction();
//        Isp isp = iSPRepository.findById(id).orElse(null);
//        Assert.isTrue(isp != null,"未找到isp:"+isp);
//        Set<User> ispMen= new HashSet<User>();
//        ispMens.stream().forEach(item -> {
//            User user= userRepository.findById(item).orElse(null);
//            ispMen.add(user);
//        });
//        isp.setIspMen(ispMen);
//        iSPRepository.save(isp);
        return null;//isp;
    }

    @Transactional
    public Isp setISPtask(UUID id, UUID taskId) {
        if(!emSei.isJoinedToTransaction())      emSei.joinTransaction();
        Isp isp = iSPRepository.findById(id).orElse(null);
        Assert.isTrue(isp != null,"未找到isp:"+isp);
        Task task = taskRepository.findById(taskId).orElse(null);
        Assert.isTrue(task != null,"未找到task:"+task);
        //todo: isp.setTask(task);
        iSPRepository.save(isp);
        return isp;
    }

    @Transactional
    public Isp setISPreport(UUID id, List<Long> reps) {
//        if(!emSei.isJoinedToTransaction())      emSei.joinTransaction();
//        Isp isp = iSPRepository.findById(id).orElse(null);
//        Assert.isTrue(isp != null,"未找到isp:"+isp);
//        reps.stream().forEach(item -> {
//            Report report= reportRepository.findById(item).orElse(null);
//            report.setIsp(isp);
//        });
//        iSPRepository.save(isp);    //对方维护关联关系，只做我方保存也可以。
        return null;// isp;
    }

    /**业务信息修改
     * 只能无厘头地把前端输入填入数据库，不去分辨前端修改哪些，前端给的空字段undefined/null输入字段也照样把DB字段设置为空,而不是看见null就保持不变的{语义不全}。java端没有undefined语义;
     * 所以只能照抄接口取值，即使是null。
     * 在这里是对整个Detail对象的全量所有字段都做更新。 需设置乐观锁，否则可能依据Hibernate缓存覆盖掉外部途径的数据库更新产生更新丢失问题。
     * */
    @MutationMapping
    @Transactional
    public Detail updateDetail(@Argument String id,@Argument("inp") DetailInput in) {
        Detail detail = entityOf(id,Detail.class);
        Assert.notNull(detail,"未找到Detail:"+id);
        //只能这么写了,nullable=false带来的麻烦啊。  若是.nsite(!Boolean.False.equals(in.getNsite()))导致缺省值是true;
        detail= ((Detail) detail).toBuilder().ident(in.getIdent()).ccost(in.getCcost()).mreprc(in.getMreprc())
                .nsite(Boolean.TRUE.equals(in.getNsite())).test(Boolean.TRUE.equals(in.getTest())).cheap(Boolean.TRUE.equals(in.getCheap()))
                .apprp(Boolean.TRUE.equals(in.getApprp())).online(Boolean.TRUE.equals(in.getOnline())).impor(Boolean.TRUE.equals(in.getImpor()))
                .ntscop(Boolean.TRUE.equals(in.getNtscop())).fcode(in.getFcode()).varea(in.getVarea()).opprn(in.getOpprn()).totm(in.getTotm())
                .diame(in.getDiame()).rustc(in.getRustc()).turbi(in.getTurbi()).hardd(in.getHardd()).phval(in.getPhval()).conduc(in.getConduc())
                .palka(in.getPalka()).totalk(in.getTotalk()).dsolid(in.getDsolid()).phosph(in.getPhosph()).oilw(in.getOilw())
                .tiron(in.getTiron()).sulfit(in.getSulfit()).rebasi(in.getRebasi()).chrion(in.getChrion()).dioxy(in.getDioxy())
                .carbon(in.getCarbon()).captem(in.getCaptem()).num(in.getNum()).isum(in.getIsum()).meter(in.getMeter())
                .zmoney(in.getZmoney())
                .build();
        detailRepository.save(detail);
        return detail;
    }
    /**‘任务金额汇总‘按钮触发的Task级别自动折扣计算
     * 不采用信任前端计算，需后端的计算，前负责显示和校验逻辑，后端负责可信计算业务逻辑。
     * */
    @MutationMapping
    @Transactional
    public TaskComResp summaryTaskFee(@Argument String id) {
//        Tool.ResolvedGuuid globalId = Tool.fromGluuId(id);
//        Task task = taskRepository.findById(globalId.getId()).orElse(null);
//        Assert.isTrue(task != null, "未找到Task:" + id);
//        var rContext = new Object() {
//            Boolean allIspOk = true;
//        };
//        task.getDets().forEach(a -> {
//            if(!a.isFeeOk())    rContext.allIspOk =false;
//        });
//        TaskComResp resp = new TaskComResp();
//        //等价#： task.getIsps().stream().allMatch(Detail::isFeeOk);
//        double totalPrc=0;
//        if(!rContext.allIspOk) {
//            resp.setWarn("有些Isp/Detail还没确认收费");
//            task.setFeeOk(false);
//            resp.setTask(task);     //立刻反馈给前端页面！
//            return resp;
//        }
//        else totalPrc= task.getDets().stream().filter(Detail::isFeeOk).mapToDouble(Detail::getSprice).sum();
//     //插入点：任务层级的折扣：?   锅炉水汽质量检验, 管道检验总长度超过,
//        task.setDreason(null);      //多个折扣理由同时有效的吗？
//        //Task归并派工限制： '1'=type && subv="1002": "电站锅炉" && OPE_TYPE":"11"; 所有Detail同时都满足才算的。
//        int countIsp=task.getDets().size();
//        if(TEST.equals(task.getBsType())) {
//            int machCount = (int) task.getDets().stream().filter(a -> null != a.getIsp() && null != a.getIsp().getDev())
//                    .filter(a -> "1".equals(a.getIsp().getDev().getType()) && "1002".equals(a.getIsp().getDev().getSubv()))
//                    .count();
//            String merg= null!=task.getDreason()? task.getDreason()+";" : "";
//            if (machCount == countIsp) {
//                if (countIsp >= 3) {
//                    totalPrc *= 0.8;
//                    task.setDreason(merg + "电站锅炉水汽质量检验时，三台及以上同时进行检验的，按收费总额的80%收取");
//                } else if (countIsp >= 2) {
//                    totalPrc *= 0.9;
//                    task.setDreason(merg + "电站锅炉水汽质量检验时，如果两台同时进行检验的，按收费总额的90%收取");
//                }
//            }
//        }
//        //"单项管道检验总长度超过10公里，不足30公里，收费标准下浮10%","单项管道检验总长度超过30公里，收费标准下浮25%"
//        //【派工限制】 同一个Task管道检验业务若有多个Detail那么必须都是管道，不可能把管道和锅炉/容器什么的混合在同一个Task中吧!。
//        int machCount = (int) task.getDets().stream().filter(a -> null != a.getIsp() && null != a.getIsp().getDev()
//                        && "8".equals(a.getIsp().getDev().getType()) )
//                .count();
//        if (machCount == countIsp && countIsp>0) {   //前置条件：全是管道检验。 监检  定期检验 年度在线 Detail  det业务锁
//            //可能getCells().会有很多单元：会影响性能？，缓存所有管道单元数据到了服务器内存来操作，而非数据库层面过滤和计算，性能负担转移。
//            //第一次执行运行时长4.86倍于第二次执行，第二次有用到了缓存，第一次必须从mySql读取Entity实体集合=List若很大的就很耗时间。若太慢考虑转queryDsl数据库层面统计查询。
//            AtomicReference<Double> lengthSum= new AtomicReference<>((double) 0);
//            task.getDets().forEach(det->{
//                    if (det.getIsp().getDev() instanceof Pipeline) {        //确保Pipeline和Eqp.type="8"一致性的;
//                        ((Pipeline) det.getIsp().getDev()).getCells().forEach(a -> {
//                            if (null != a.getDet() && det.equals(a.getDet()) && null != a.getLeng() && a.getLeng() > 0) {
//                                lengthSum.updateAndGet(v -> (double) (v + a.getLeng()));
//                            }
//                        });
//                    }
//               }
//            );
//            double  taskPipeLength=lengthSum.get();
//            String merg= null!=task.getDreason()? task.getDreason()+";" : "";
//            if (taskPipeLength > 30000) {
//                totalPrc *= 0.75;
//                task.setDreason(merg + "单项管道检验总长度超过30公里，收费标准下浮25%");
//            } else if (taskPipeLength > 10000) {
//                totalPrc *= 0.9;        //可允许：折上折，两次折扣的可能。
//                task.setDreason(merg + "单项管道检验总长度超过10公里，不足30公里，收费标准下浮10%");
//            }
//        }
//        task.setDisfee(totalPrc);
//        task.setFeeOk(false);
//        resp.setTask(task);
        return null;//resp;
    }
    /**确认Task的实际收费金额*/
    @MutationMapping
    @Transactional
    public TaskComResp confirmTaskFee(@Argument String id,@Argument("inp") FeeItemInput in) {
        Tool.ResolvedGuuid globalId = Tool.fromGluuId(id);
        Task task = taskRepository.findById(globalId.getId()).orElse(null);
        Assert.isTrue(task != null, "未找到Task:" + id);
        TaskComResp resp = new TaskComResp();
        //TODO: 发票已经开出，不允许修改收费？   反悔措施,可重复修改已经确认的收费吗？？
        if(null==task.getDisfee())  resp.setWarn("还未任务金额汇总");
        else if(null==in.getAmount() || in.getAmount()<0)  resp.setWarn("非法金额");
        else if(in.getAmount()<task.getDisfee() && !StringUtils.hasText(in.getMemo()))   resp.setWarn("须有审批理由");
        if(null!=resp.getWarn())    return resp;
        //认定为正常收费
        task.setCharge(in.getAmount());
        //确认完成？ 插入流程: todo：//审批, 被批准之后，才能setFeeOk！
        task.setFeeOk(true);
        resp.setTask(task);
        return resp;
    }
}



/*
加了cache缓存后，为了在事务中读取数据库最新数据：emSei.find(Eqp.class,id)或eQPRepository.findById(id)或eQPRepository.getOne(id)或findAll()；
                或eQPRepository.findAll(new Specification<Eqp>() {@Override },pageable);
必须加  emSei.setProperty(JPA_SHARED_CACHE_RETRIEVE_MODE, CacheRetrieveMode.BYPASS);
        emSei.setProperty(JPA_SHARED_CACHE_STORE_MODE, CacheStoreMode.REFRESH); 加了这2条才能从DB去取最新数据。
而这些方法无需添加也能去数据库取最新数据：eQPRepository.findByCod(cod)或emSei.createQuery("")
*/

